package com.gitee.wsl.ext.base


import com.gitee.wsl.base.Objects.hashCode
import com.gitee.wsl.exception.OutOfMemoryError
import com.gitee.wsl.ext.array.binarySearch
import com.ionspin.kotlin.bignum.integer.BigInteger
import kotlin.experimental.inv
import kotlin.math.max
import kotlin.math.min

val Array<*>.length get() = size


object Arrays {

    private const val INITIAL_CAPACITY = 8

    /**
     * The maximum array size for the methods in this class: {@value}.
     */
    private val MAX_ARRAY_SIZE: Int = Int.Companion.MAX_VALUE - 8

    /**
     * Ensure the array can add an element at the specified index.
     *
     * @param array the array to check.
     * @param index the index to add at.
     * @return the array or a newly allocated copy of the array.
     */
    fun ensureCapacityForAdd(array: IntArray, index: Int): IntArray {
        if (index >= array.size) {
            return copyOf(array, min(MAX_ARRAY_SIZE.toLong(), max(array.size * 2L, (index + 1).toLong())).toInt())
        }
        return array
    }

    @Suppress("UNCHECKED_CAST")
    fun <T> newArray(): Array<T?> = arrayOfNulls<Any?>(INITIAL_CAPACITY) as Array<T?>

    //class Array2DEntry<T:Any>(val row: Int,val col: Int,val value:T)

    inline fun <reified T> createArray(row: Int, col: Int, createFun:(row: Int, col: Int)->T) = Array(row) {
            rowE -> Array(col) { colE -> createFun(rowE, colE) }
    }
        /*
         * 对于二维数组，每个元素是一个一维数组。
         * 双层for循环，外层行、内层列方式遍历； 每输出一行后，换行。
         * 每一行上的元素数量 = col 的值，
         * 每一列上的元素数量 = row 的值。
         *
         * 每输出的一行元素，对应实际问题中的，如图片、棋盘等，表示的是 它们的 一列元素。
         */
        fun <T> dumpArray(array: Array<Array<T>>, forCallFun:( row: Int, col: Int,value:T)->Unit) {
            for (rowIndex in array.indices) {
                for (colIndex in array[rowIndex].indices) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

        /**
         * 对于二维数组，每个元素是一个一维数组。
         * 双层for循环，外层列、内层行方式遍历； 每输出一行后，换行。
         * 每一行上的元素数量 = row 的值，  行上的元素 = dumpArray(array) 列上的元素。
         * 每一列上的元素数量 = col 的值，  列上的元素 = dumpArray(array) 行上的元素。
         *
         * 每输出的一行元素，对应实际问题中的，如图片、棋盘等，表示的是 它们的 一行元素。
         *
         * 两种方式，均可以将 二维数组的每个元素遍历到。
         * 本函数的输出，看起来像是 dumpArray 函数输出 右(顺时针)旋转了90度，并且左右逆向了
         */
        fun <T> dumpArray2(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {
            for (colIndex in 0 until col) {
                for (rowIndex in 0 until row) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

        /**
         * dumpArray输出，直接 右(顺时针)旋转90度。
         * 新的第一行数据是 0(3,0) 0(2,0) 0(1,0) 0(0,0)  ， 与原第一列的数据 0(0,0) 0(1,0) 0(2,0) 0(3,0)  左右逆向了
         * 结果，x横向递减，y纵向递增
         *
         * 对外行，内列，顺时针旋转90度，与 '对外列，内行，左右逆序' 结果一样。编号 A
         */
        fun <T> dumpArrayRotateClockwise(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {
            for (colIndex in 0 until col) {
                for (rowIndex in 0 until row) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

        /*
         * dumpArray输出，直接 左(逆时针)旋转90度。
         * 新的第一行数据是 0(0,4) 0(1,4) 0(2,4) 0(3,4)  ， 与原最后一列的数据 相同
         *
         * 结果，x横向递增，y纵向递减
         */
        fun <T> dumpArrayRotateAnticlockwise(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {
            //println("对外行，内列，逆时针旋转90度，与 '外列，内行 上下逆序' 结果一样。编号 D")
            for (colIndex in 0 until col) {
                for (rowIndex in 0 until row) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

         //对外列，内行，顺时针旋转90度, 与 '对外行，内列，左右逆序' 结果一样。编号 B
        fun <T> dumpArray2RotateClockwise(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {
            for (rowIndex in 0 until row) {
                for (colIndex in 0 until col) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

        /**
         *对外列，内行，逆时针旋转90度，与 '对外行，内列 上下逆序' 结果一样。编号 C
         * 第一行变成 0(3,0) 0(3,1) 0(3,2) 0(3,3) 0(3,4)
         */
        fun <T> dumpArray2RotateAnticlockwise(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {
            for (rowIndex in 0 until row) {
                for (colIndex in 0 until col) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }

        }

        //对外行，内列，左右逆序，与 '对外列，内行，顺时针旋转90度' 结果一样。编号 B
        fun <T> dumpArrayReverseRc(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {

            for (rowIndex in 0 until row) {
                for (colIndex in 0 until col) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

        //对外列，内行，左右逆序，与 `对外行，内列，顺时针旋转90度` 结果一样。编号 A
        fun <T> dumpArray2ReverseCr(array: Array<Array<T>>, row: Int, col: Int,forCallFun:( row: Int, col: Int,value:T)->Unit) {
            for (colIndex in 0 until col) {
                for (rowIndex in 0 until row) {
                    forCallFun(rowIndex,colIndex,array[rowIndex][colIndex])
                }
            }
        }

    fun joins(prefix: ByteArray, suffix: ByteArray): ByteArray {
        val ret = ByteArray(prefix.size + suffix.size)
        arraycopy(prefix, 0, ret, 0, prefix.size)
        arraycopy(suffix, 0, ret, prefix.size, suffix.size)
        return ret
    }

    /**
     * 二维网格迁移
     * 给你一个 m 行 n 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。
     * 每次「迁移」操作将会引发下述活动：
     *
     * 位于 grid[i][j] 的元素将会移动到 grid[i][j + 1]。
     * 位于 grid[i][n - 1] 的元素将会移动到 grid[i + 1][0]。
     * 位于 grid[m - 1][n - 1] 的元素将会移动到 grid[0][0]。
     * 请你返回 k 次迁移操作后最终得到的 二维网格。
     *
     * 输入：grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1
     * 输出：[[9,1,2],[3,4,5],[6,7,8]]
     */
    fun shiftGrid(grid: Array<IntArray>, k: Int): List<List<Int>> {
        val heightT = grid.size
        val widthT = if (grid.isNotEmpty()) grid[0].size else 0

        val result = ArrayList<ArrayList<Int>>()
        grid.forEach {
            val element = ArrayList<Int>()
            result.add(element)
            it.forEach { _ ->
                element.add(0)
            }
        }

        grid.forEachIndexed { height, ints ->
            ints.forEachIndexed { width, i ->
                val newIndex = (height * widthT + width + k) % (heightT * widthT)
                result[newIndex / widthT][newIndex % widthT] = i
            }
        }
        return result
    }

    /**
     * Reverses the order of the elements in the specified range within the given array.
     *
     * @param <V> the type of elements in the array
     * @param arr the array
     * @param from the index of the first element (inclusive) inside the range to reverse
     * @param to the index of the last element (inclusive) inside the range to reverse
    </V> */
    fun <V> reverse(arr: Array<V>, from: Int, to: Int) {
        var i = from
        var j = to
        while (i < j) {
            swap(arr, i, j)
            ++i
            --j
        }
    }

    /**
     * Reverses the order of the elements in the specified range within the given array.
     *
     * @param arr the array
     * @param from the index of the first element (inclusive) inside the range to reverse
     * @param to the index of the last element (inclusive) inside the range to reverse
     */
    fun reverse(arr: IntArray, from: Int, to: Int) {
        var i = from
        var j = to
        while (i < j) {
            val tmp = arr[j]
            arr[j] = arr[i]
            arr[i] = tmp
            ++i
            --j
        }
    }

    /**
     * Swaps the two elements at the specified indices in the given array.
     *
     * @param <V> the type of elements in the array
     * @param arr the array
     * @param i the index of the first element
     * @param j the index of the second element
    </V> */
    fun <V> swap(arr: Array<V>, i: Int, j: Int) {
        val tmp = arr[j]
        arr[j] = arr[i]
        arr[i] = tmp
    }

    fun <V> arraycopy(src:Array<V>, srcPos:Int, dest:Array<V>, destPos:Int, length:Int){
        src.copyInto(dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:ShortArray,srcPos:Int, dest:ShortArray,  destPos:Int, length:Int){
        src.copyInto(dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:IntArray,srcPos:Int, dest:IntArray,  destPos:Int, length:Int){
        src.copyInto(dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:LongArray,srcPos:Int, dest:LongArray,  destPos:Int, length:Int){
        src.copyInto( dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:FloatArray,srcPos:Int, dest:FloatArray,  destPos:Int, length:Int){
        src.copyInto( dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:DoubleArray,srcPos:Int, dest:DoubleArray,  destPos:Int, length:Int){
        src.copyInto( dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:ByteArray,srcPos:Int, dest:ByteArray,  destPos:Int, length:Int){
        src.copyInto( dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:CharArray,srcPos:Int, dest:CharArray,  destPos:Int, length:Int){
        src.copyInto( dest,destPos,srcPos,srcPos+length)
    }

    fun  arraycopy( src:BooleanArray,srcPos:Int, dest:BooleanArray,  destPos:Int, length:Int){
        src.copyInto( dest,destPos,srcPos,srcPos+length)
    }

    /** Copies [size] elements of [src] starting at [srcPos] into [dst] at [dstPos]  */
    fun <T> arraycopy(src: List<T>, srcPos: Int, dst: MutableList<T>, dstPos: Int, size: Int) {
        if (src === dst) error("Not supporting the same array")
        for (n in 0 until size) {
            dst[dstPos + n] = src[srcPos]
        }
    }

    inline fun <T> arraycopy(
        size: Int,
        src: Any?,
        srcPos: Int,
        dst: Any?,
        dstPos: Int,
        setDst: (Int, T) -> Unit,
        getSrc: (Int) -> T,
    ) {
        val overlapping = src === dst && dstPos > srcPos
        if (overlapping) {
            var n = size
            while (--n >= 0) setDst(dstPos + n, getSrc(srcPos + n))
        } else {
            for (n in 0 until size) setDst(dstPos + n, getSrc(srcPos + n))
        }
    }


    fun areAllZeroes(buf: ByteArray, off: Int, len: Int): Boolean {
        var bits = 0
        for (i in 0..<len) {
            bits = bits or buf[off + i].toInt()
        }
        return bits == 0
    }

    fun areEqual(a: BooleanArray?, b: BooleanArray?): Boolean {
        return a.contentEquals(b)
    }

    fun areEqual(a: ByteArray?, b: ByteArray?): Boolean {
        return a.contentEquals(b)
    }

    fun areEqual(a: ByteArray, aFromIndex: Int, aToIndex: Int, b: ByteArray, bFromIndex: Int, bToIndex: Int): Boolean {
        val aLength = aToIndex - aFromIndex
        val bLength = bToIndex - bFromIndex

        if (aLength != bLength) {
            return false
        }

        for (i in 0..<aLength) {
            if (a[aFromIndex + i] != b[bFromIndex + i]) {
                return false
            }
        }

        return true
    }

    fun areEqual(a: CharArray?, b: CharArray?): Boolean {
        return a.contentEquals(b)
    }

    fun areEqual(a: IntArray?, b: IntArray?): Boolean {
        return a.contentEquals(b)
    }

    fun areEqual(a: LongArray?, b: LongArray?): Boolean {
        return a.contentEquals(b)
    }

    fun areEqual(a: Array<Any?>?, b: Array<Any?>?): Boolean {
        return a.contentEquals(b)
    }

    fun areEqual(a: ShortArray?, b: ShortArray?): Boolean {
        return a.contentEquals(b)
    }

    /**
     * A constant time equals comparison - does not terminate early if
     * test will fail. For best results always pass the expected value
     * as the first parameter.
     *
     * @param expected first array
     * @param supplied second array
     * @return true if arrays equal, false otherwise.
     */
    fun constantTimeAreEqual(
        expected: ByteArray?,
        supplied: ByteArray?,
    ): Boolean {
        if (expected == null || supplied == null) {
            return false
        }

        if (expected === supplied) {
            return true
        }

        val len = if (expected.size < supplied.size) expected.size else supplied.size

        var nonEqual = expected.size xor supplied.size

        for (i in 0..<len) {
            nonEqual = nonEqual or (expected[i].toInt() xor supplied[i].toInt())
        }
        for (i in len..<supplied.size) {
            nonEqual = nonEqual or (supplied[i].toInt() xor supplied[i].inv().toInt())
        }

        return nonEqual == 0
    }

    fun constantTimeAreEqual(len: Int, a: ByteArray, aOff: Int, b: ByteArray, bOff: Int): Boolean {
        require(len >= 0) { "'len' cannot be negative" }
        if (aOff > (a.size - len)) {
            throw IndexOutOfBoundsException("'aOff' value invalid for specified length")
        }
        if (bOff > (b.size - len)) {
            throw IndexOutOfBoundsException("'bOff' value invalid for specified length")
        }

        var d = 0
        for (i in 0..<len) {
            d = d or (a[aOff + i].toInt() xor b[bOff + i].toInt())
        }
        return 0 == d
    }

    /**
     * A constant time equals comparison - does not terminate early if
     * comparison fails. For best results always pass the expected value
     * as the first parameter.
     *
     * @param expected first array
     * @param supplied second array
     * @return true if arrays equal, false otherwise.
     */
    fun constantTimeAreEqual(
        expected: CharArray?,
        supplied: CharArray?,
    ): Boolean {
        if (expected == null || supplied == null) {
            return false
        }

        if (expected === supplied) {
            return true
        }

        val len = min(expected.size, supplied.size)

        var nonEqual = expected.size xor supplied.size

        // do the char-wise comparison
        for (i in 0..<len) {
            nonEqual = nonEqual or (expected[i].code xor supplied[i].code)
        }
        // If supplied is longer than expected, iterate over rest of supplied with NOPs
        for (i in len..<supplied.size) {
            nonEqual = nonEqual or (supplied[i].code.toByte().toInt() xor supplied[i].code.toByte().inv().toInt())
        }

        return nonEqual == 0
    }

    fun compareUnsigned(a: ByteArray?, b: ByteArray?): Int {
        if (a === b) {
            return 0
        }
        if (a == null) {
            return -1
        }
        if (b == null) {
            return 1
        }
        val minLen = min(a.size, b.size)
        for (i in 0..<minLen) {
            val aVal = a[i].toInt() and 0xFF
            val bVal = b[i].toInt() and 0xFF
            if (aVal < bVal) {
                return -1
            }
            if (aVal > bVal) {
                return 1
            }
        }
        if (a.size < b.size) {
            return -1
        }
        if (a.size > b.size) {
            return 1
        }
        return 0
    }

    fun contains(a: BooleanArray, `val`: Boolean): Boolean {
        for (i in a.indices) {
            if (a[i] == `val`) {
                return true
            }
        }
        return false
    }

    fun contains(a: ByteArray, `val`: Byte): Boolean {
        for (i in a.indices) {
            if (a[i] == `val`) {
                return true
            }
        }
        return false
    }

    fun contains(a: CharArray, `val`: Char): Boolean {
        for (i in a.indices) {
            if (a[i] == `val`) {
                return true
            }
        }
        return false
    }

    fun contains(a: IntArray, `val`: Int): Boolean {
        for (i in a.indices) {
            if (a[i] == `val`) {
                return true
            }
        }
        return false
    }

    fun contains(a: LongArray, `val`: Long): Boolean {
        for (i in a.indices) {
            if (a[i] == `val`) {
                return true
            }
        }
        return false
    }

    fun contains(a: ShortArray, `val`: Short): Boolean {
        for (i in a.indices) {
            if (a[i] == `val`) {
                return true
            }
        }
        return false
    }

    fun fill(a: BooleanArray, `val`: Boolean) {
        fill(a, `val`)
    }

    fun fill(a: BooleanArray, fromIndex: Int, toIndex: Int, `val`: Boolean) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun fill(a: ByteArray, `val`: Byte) {
        fill(a, `val`)
    }

    fun fill(a: ByteArray, fromIndex: Int, toIndex: Int, `val`: Byte) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun fill(a: CharArray, `val`: Char) {
        fill(a, `val`)
    }

    fun fill(a: CharArray, fromIndex: Int, toIndex: Int, `val`: Char) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun fill(a: IntArray, `val`: Int) {
        fill(a, `val`)
    }

    fun fill(a: IntArray, fromIndex: Int, toIndex: Int, `val`: Int) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun fill(a: LongArray, `val`: Long) {
        fill(a, `val`)
    }

    fun fill(a: LongArray, fromIndex: Int, toIndex: Int, `val`: Long) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun fill(a: Array<Any?>, `val`: Any?) {
        fill(a, `val`)
    }

    fun fill(a: Array<Any?>, fromIndex: Int, toIndex: Int, `val`: Any?) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun fill(a: ShortArray, `val`: Short) {
        fill(a, `val`)
    }

    fun fill(a: ShortArray, fromIndex: Int, toIndex: Int, `val`: Short) {
        fill(a, fromIndex, toIndex, `val`)
    }

    fun hashCode(data: ByteArray?): Int {
        if (data == null) {
            return 0
        }

        var i = data.size
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor data[i].toInt()
        }

        return hc
    }

    fun hashCode(data: ByteArray?, off: Int, len: Int): Int {
        if (data == null) {
            return 0
        }

        var i = len
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor data[off + i].toInt()
        }

        return hc
    }

    fun hashCode(data: CharArray?): Int {
        if (data == null) {
            return 0
        }

        var i = data.size
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor data[i].code
        }

        return hc
    }

    fun hashCode(ints: Array<IntArray?>): Int {
        var hc = 0

        for (i in ints.indices) {
            hc = hc * 257 + hashCode(ints[i])
        }

        return hc
    }

    fun hashCode(data: IntArray?): Int {
        if (data == null) {
            return 0
        }

        var i = data.size
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor data[i]
        }

        return hc
    }

    fun hashCode(data: IntArray?, off: Int, len: Int): Int {
        if (data == null) {
            return 0
        }

        var i = len
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor data[off + i]
        }

        return hc
    }

    fun hashCode(data: LongArray?): Int {
        if (data == null) {
            return 0
        }

        var i = data.size
        var hc = i + 1

        while (--i >= 0) {
            val di = data[i]
            hc *= 257
            hc = hc xor di.toInt()
            hc *= 257
            hc = hc xor (di ushr 32).toInt()
        }

        return hc
    }

    fun hashCode(data: LongArray?, off: Int, len: Int): Int {
        if (data == null) {
            return 0
        }

        var i = len
        var hc = i + 1

        while (--i >= 0) {
            val di = data[off + i]
            hc *= 257
            hc = hc xor di.toInt()
            hc *= 257
            hc = hc xor (di ushr 32).toInt()
        }

        return hc
    }

    fun hashCode(shorts: Array<Array<ShortArray?>?>): Int {
        var hc = 0

        for (i in shorts.indices) {
            hc = hc * 257 + hashCode(shorts[i])
        }

        return hc
    }

    fun hashCode(shorts: Array<ShortArray?>): Int {
        var hc = 0

        for (i in shorts.indices) {
            hc = hc * 257 + hashCode(shorts[i])
        }

        return hc
    }

    fun hashCode(data: ShortArray?): Int {
        if (data == null) {
            return 0
        }

        var i = data.size
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor (data[i].toInt() and 0xff)
        }

        return hc
    }

    fun hashCode(data: Array<Any?>?): Int {
        if (data == null) {
            return 0
        }

        var i = data.size
        var hc = i + 1

        while (--i >= 0) {
            hc *= 257
            hc = hc xor hashCode(data[i])
        }

        return hc
    }

    fun clone(data: BooleanArray?): BooleanArray? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: ByteArray?): ByteArray? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: CharArray?): CharArray? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: IntArray?): IntArray? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: LongArray?): LongArray? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: ShortArray?): ShortArray? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: Array<BigInteger?>?): Array<BigInteger?>? {
        return if (null == data) null else data.clone()
    }

    fun clone(data: ByteArray?, existing: ByteArray?): ByteArray? {
        if (data == null) {
            return null
        }
        if ((existing == null) || (existing.size != data.size)) {
            return clone(data)
        }
        arraycopy(data, 0, existing, 0, existing.size)
        return existing
    }

    fun clone(data: LongArray?, existing: LongArray?): LongArray? {
        if (data == null) {
            return null
        }
        if ((existing == null) || (existing.size != data.size)) {
            return clone(data)
        }
        arraycopy(data, 0, existing, 0, existing.size)
        return existing
    }

    fun clone(data: Array<ByteArray?>?): Array<ByteArray?>? {
        if (data == null) {
            return null
        }

        val copy = kotlin.arrayOfNulls<ByteArray>(data.size)

        for (i in copy.indices) {
            copy[i] = clone(data[i])
        }

        return copy
    }

    fun clone(data: Array<Array<ByteArray?>?>?): Array<Array<ByteArray?>?>? {
        if (data == null) {
            return null
        }

        val copy = kotlin.arrayOfNulls<Array<ByteArray?>>(data.size)

        for (i in copy.indices) {
            copy[i] = clone(data[i])
        }

        return copy
    }

    fun copyOf(original: BooleanArray, newLength: Int): BooleanArray {
        val copy = BooleanArray(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOf(original: ByteArray, newLength: Int): ByteArray {
        val copy = ByteArray(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOf(original: CharArray, newLength: Int): CharArray {
        val copy = CharArray(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOf(original: IntArray, newLength: Int): IntArray {
        val copy = IntArray(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOf(original: LongArray, newLength: Int): LongArray {
        val copy = LongArray(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOf(original: ShortArray, newLength: Int): ShortArray {
        val copy = ShortArray(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOf(original: Array<BigInteger?>, newLength: Int): Array<BigInteger?> {
        val copy: Array<BigInteger?> = kotlin.arrayOfNulls<BigInteger>(newLength)
        arraycopy(original, 0, copy, 0, min(original.size, newLength))
        return copy
    }

    fun copyOfRange(original: BooleanArray, from: Int, to: Int): BooleanArray {
        val newLength = getLength(from, to)
        val copy = BooleanArray(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    /**
     * Make a copy of a range of bytes from the passed in array. The range can extend beyond the end
     * of the input array, in which case the returned array will be padded with zeroes.
     *
     * @param original the array from which the data is to be copied.
     * @param from     the start index at which the copying should take place.
     * @param to       the final index of the range (exclusive).
     * @return a new byte array containing the range given.
     */
    fun copyOfRange(original: ByteArray, from: Int, to: Int): ByteArray {
        val newLength = getLength(from, to)
        val copy = ByteArray(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    fun copyOfRange(original: CharArray, from: Int, to: Int): CharArray {
        val newLength = getLength(from, to)
        val copy = CharArray(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    fun copyOfRange(original: IntArray, from: Int, to: Int): IntArray {
        val newLength = getLength(from, to)
        val copy = IntArray(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    fun copyOfRange(original: LongArray, from: Int, to: Int): LongArray {
        val newLength = getLength(from, to)
        val copy = LongArray(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    fun copyOfRange(original: ShortArray, from: Int, to: Int): ShortArray {
        val newLength = getLength(from, to)
        val copy = ShortArray(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    fun copyOfRange(original: Array<BigInteger?>, from: Int, to: Int): Array<BigInteger?> {
        val newLength = getLength(from, to)
        val copy: Array<BigInteger?> = kotlin.arrayOfNulls<BigInteger>(newLength)
        arraycopy(original, from, copy, 0, min(original.size - from, newLength))
        return copy
    }

    private fun getLength(from: Int, to: Int): Int {
        val newLength = to - from
        require(newLength >= 0) { from.toString() + " > " + to }
        return newLength
    }

    fun append(a: ByteArray?, b: Byte): ByteArray {
        if (a == null) {
            return byteArrayOf(b)
        }

        val length = a.size
        val result = ByteArray(length + 1)
        arraycopy(a, 0, result, 0, length)
        result[length] = b
        return result
    }

    fun append(a: ShortArray?, b: Short): ShortArray {
        if (a == null) {
            return shortArrayOf(b)
        }

        val length = a.size
        val result = ShortArray(length + 1)
        arraycopy(a, 0, result, 0, length)
        result[length] = b
        return result
    }

    fun append(a: IntArray?, b: Int): IntArray {
        if (a == null) {
            return intArrayOf(b)
        }

        val length = a.size
        val result = IntArray(length + 1)
        arraycopy(a, 0, result, 0, length)
        result[length] = b
        return result
    }

    fun append(a: Array<String?>?, b: String?): Array<String?> {
        if (a == null) {
            return arrayOf<String?>(b)
        }

        val length = a.size
        val result = kotlin.arrayOfNulls<String>(length + 1)
        arraycopy(a, 0, result, 0, length)
        result[length] = b
        return result
    }

    fun concatenate(a: ByteArray?, b: ByteArray?): ByteArray? {
        if (null == a) {
            // b might also be null
            return clone(b)
        }
        if (null == b) {
            // a might also be null
            return clone(a)
        }

        val r = ByteArray(a.size + b.size)
        arraycopy(a, 0, r, 0, a.size)
        arraycopy(b, 0, r, a.size, b.size)
        return r
    }

    fun concatenate(a: ShortArray?, b: ShortArray?): ShortArray? {
        if (null == a) {
            // b might also be null
            return clone(b)
        }
        if (null == b) {
            // a might also be null
            return clone(a)
        }

        val r = ShortArray(a.size + b.size)
        arraycopy(a, 0, r, 0, a.size)
        arraycopy(b, 0, r, a.size, b.size)
        return r
    }

    fun concatenate(a: ByteArray?, b: ByteArray?, c: ByteArray?): ByteArray? {
        if (null == a) {
            return concatenate(b, c)
        }
        if (null == b) {
            return concatenate(a, c)
        }
        if (null == c) {
            return concatenate(a, b)
        }

        val r = ByteArray(a.size + b.size + c.size)
        var pos = 0
        arraycopy(a, 0, r, pos, a.size)
        pos += a.size
        arraycopy(b, 0, r, pos, b.size)
        pos += b.size
        arraycopy(c, 0, r, pos, c.size)
        return r
    }


    /**
     * Perform an insertion of the specified value into the sorted array.
     *
     * If the value is already present in the array then no insertion will be performed and the
     * same array will be returned.  If the value is not present then a new array will be created
     * and will contain the specified value in the appropriate location.
     *
     * @param an  the array to insert into, may be null
     * @param n   the value to insert
     *
     * @return the resulting array.
     */
    fun binaryInsert(an: IntArray?, n: Int): IntArray {
        if (an == null) {
            return intArrayOf(n)
        }

        //var of: Int = binarySearch(an, n)
        val search = an.binarySearch(n)
        if (!search.found) {
            // insertion required
            val of = -search.nearIndex // correct offset to represent the insertion point, or rather point just after it

            val anNew = IntArray(an.size + 1)
            arraycopy(an, 0, anNew, 0, of - 1)
            anNew[of - 1] = n
            arraycopy(an, of - 1, anNew, of, an.size - of + 1)

            return anNew
        }

        // else; already present
        return an
    }


    fun concatenate(a: ByteArray?, b: ByteArray?, c: ByteArray?, d: ByteArray?): ByteArray? {
        if (null == a) {
            return concatenate(b, c, d)
        }
        if (null == b) {
            return concatenate(a, c, d)
        }
        if (null == c) {
            return concatenate(a, b, d)
        }
        if (null == d) {
            return concatenate(a, b, c)
        }

        val r = ByteArray(a.size + b.size + c.size + d.size)
        var pos = 0
        arraycopy(a, 0, r, pos, a.size)
        pos += a.size
        arraycopy(b, 0, r, pos, b.size)
        pos += b.size
        arraycopy(c, 0, r, pos, c.size)
        pos += c.size
        arraycopy(d, 0, r, pos, d.size)
        return r
    }

    fun concatenate(arrays: Array<ByteArray>): ByteArray {
        var size = 0
        for (i in arrays.indices) {
            size += arrays[i].size
        }

        val rv = ByteArray(size)

        var offSet = 0
        for (i in arrays.indices) {
            arraycopy(arrays[i], 0, rv, offSet, arrays[i].size)
            offSet += arrays[i].size
        }

        return rv
    }

    fun concatenate(a: IntArray?, b: IntArray?): IntArray? {
        if (null == a) {
            // b might also be null
            return clone(b)
        }
        if (null == b) {
            // a might also be null
            return clone(a)
        }

        val r = IntArray(a.size + b.size)
        arraycopy(a, 0, r, 0, a.size)
        arraycopy(b, 0, r, a.size, b.size)
        return r
    }

    fun prepend(a: ByteArray?, b: Byte): ByteArray {
        if (a == null) {
            return byteArrayOf(b)
        }

        val length = a.size
        val result = ByteArray(length + 1)
        arraycopy(a, 0, result, 1, length)
        result[0] = b
        return result
    }

    fun prepend(a: ShortArray?, b: Short): ShortArray {
        if (a == null) {
            return shortArrayOf(b)
        }

        val length = a.size
        val result = ShortArray(length + 1)
        arraycopy(a, 0, result, 1, length)
        result[0] = b
        return result
    }

    fun prepend(a: IntArray?, b: Int): IntArray {
        if (a == null) {
            return intArrayOf(b)
        }

        val length = a.size
        val result = IntArray(length + 1)
        arraycopy(a, 0, result, 1, length)
        result[0] = b
        return result
    }

    fun reverse(a: ByteArray?): ByteArray? {
        if (a == null) {
            return null
        }

        var p1 = 0
        var p2 = a.size
        val result = ByteArray(p2)

        while (--p2 >= 0) {
            result[p2] = a[p1++]
        }

        return result
    }

    fun reverse(a: IntArray?): IntArray? {
        if (a == null) {
            return null
        }

        var p1 = 0
        var p2 = a.size
        val result = IntArray(p2)

        while (--p2 >= 0) {
            result[p2] = a[p1++]
        }

        return result
    }

    fun reverse(input: ByteArray, output: ByteArray) {
        val last = input.size - 1
        for (i in 0..last) {
            output[i] = input[last - i]
        }
    }

    fun reverseInPlace(a: ByteArray?): ByteArray? {
        if (null == a) {
            return null
        }

        var p1 = 0
        var p2 = a.size - 1
        while (p1 < p2) {
            val t1 = a[p1]
            val t2 = a[p2]
            a[p1++] = t2
            a[p2--] = t1
        }

        return a
    }

    fun reverseInPlace(a: ByteArray, aOff: Int, aLen: Int) {
        var p1 = aOff
        var p2 = aOff + aLen - 1
        while (p1 < p2) {
            val t1 = a[p1]
            val t2 = a[p2]
            a[p1++] = t2
            a[p2--] = t1
        }
    }

    fun reverseInPlace(a: ShortArray?): ShortArray? {
        if (null == a) {
            return null
        }

        var p1 = 0
        var p2 = a.size - 1
        while (p1 < p2) {
            val t1 = a[p1]
            val t2 = a[p2]
            a[p1++] = t2
            a[p2--] = t1
        }

        return a
    }

    fun reverseInPlace(a: IntArray?): IntArray? {
        if (null == a) {
            return null
        }

        var p1 = 0
        var p2 = a.size - 1
        while (p1 < p2) {
            val t1 = a[p1]
            val t2 = a[p2]
            a[p1++] = t2
            a[p2--] = t1
        }

        return a
    }


    /**
     * Fill a section of the array with zeros
     * @param data the array to fill
     * @param start start index
     * @param len length
     */
    fun clear(data: ByteArray?, start: Int, len: Int) {
        if (data != null) {
            fill(data, start, start + len, 0.toByte())
        }
    }

    /**
     * Fill input array by zeros
     *
     * @param data input array
     */
    fun clear(data: ByteArray?) {
        if (null != data) {
            fill(data, 0x00.toByte())
        }
    }

    fun clear(data: IntArray?) {
        if (null != data) {
            fill(data, 0)
        }
    }


    fun clear(data: Array<IntArray>?) {
        if (null != data) {
            for (i in data) {
                fill(i, 0)
            }
        }
    }


    fun clear(data: Array<LongArray>?) {
        if (null != data) {
            for (i in data) {
                fill(i, 0)
            }
        }
    }


    fun isNullOrContainsNull(array: Array<Any?>?): Boolean {
        if (null == array) {
            return true
        }
        val count = array.size
        for (i in 0..<count) {
            if (null == array[i]) {
                return true
            }
        }
        return false
    }

    fun isNullOrEmpty(array: ByteArray?): Boolean {
        return null == array || array.isEmpty()
    }

    fun isNullOrEmpty(array: IntArray?): Boolean {
        return null == array || array.isEmpty()
    }

    fun isNullOrEmpty(array: Array<Any?>?): Boolean {
        return null == array || array.isEmpty()
    }

    fun arrayCopyAndAppend(src: Array<Any?>, appended: Any?): Array<Any?> {
        val dst = copyToLength(src, src.size + 1)
        dst[src.size] = appended

        return dst
    }

    fun arrayCopyAndAppend(src: Array<Any?>, first: Any?, second: Any?): Array<Any?> {
        val dst = copyToLength(src, src.size + 2)
        dst[src.size] = first
        dst[src.size + 1] = second

        return dst
    }

    fun arrayCopyAndInsert(src: Array<Any?>, index: Int, value: Any?): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(src.size + 1)

        arraycopy(src, 0, dst, 0, index)
        dst[index] = value
        arraycopy(src, index, dst, index + 1, src.size - index)

        return dst
    }

    fun arrayCopyAndReplace(src: Array<Any?>, index: Int, value: Any?): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(src.size)
        arraycopy(src, 0, dst, 0, src.size)
        dst[index] = value
        return dst
    }

    fun arrayCopyAndReplacePair(src: Array<Any?>, index: Int, first: Any?, second: Any?): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(src.size)
        arraycopy(src, 0, dst, 0, src.size)
        dst[index] = first
        dst[index + 1] = second
        return dst
    }

    fun arrayCopyAndInsert(src: Array<Any?>, index: Int, key: Any?, value: Any?): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(src.size + 2)

        arraycopy(src, 0, dst, 0, index)
        dst[index] = key
        dst[index + 1] = value
        arraycopy(src, index, dst, index + 2, src.size - index)

        return dst
    }

    fun arrayCopyAndInsert(src: IntArray, index: Int, value: Int): IntArray {
        val dst = IntArray(src.size + 1)

        arraycopy(src, 0, dst, 0, index)
        dst[index] = value
        arraycopy(src, index, dst, index + 1, src.size - index)

        return dst
    }

    fun arrayCopyAndRemove(src: Array<Any?>, index: Int): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(src.size - 1)

        val indexAfter = index + 1
        arraycopy(src, 0, dst, 0, index)
        arraycopy(src, indexAfter, dst, index, src.size - (indexAfter))

        return dst
    }

    fun arrayCopyAndRemove(src: IntArray, index: Int): IntArray {
        val dst = IntArray(src.size - 1)

        val indexAfter = index + 1
        arraycopy(src, 0, dst, 0, index)
        arraycopy(src, indexAfter, dst, index, src.size - (indexAfter))

        return dst
    }

    fun arrayCopyAndRemovePair(src: Array<Any?>, index: Int): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(src.size - 2)

        val indexAfter = index + 2
        arraycopy(src, 0, dst, 0, index)
        arraycopy(src, indexAfter, dst, index, src.size - (indexAfter))

        return dst
    }

    /**
     * A strangely specific array copy. Remove two elements at `removeIndex`
     * and `removeIndex+1`; and insert the given value at `addIndex`.
     * All indices are in terms of the *source* array.
     *
     *
     * The added index may not be within the removed indices.
     */
    fun arrayCopyAndRemovePairAndInsert(
        src: Array<Any?>,
        removeIndex: Int,
        addIndex: Int,
        insertion: Any?,
    ): Array<Any?> {
        // The copy is made in three chunks (the pipes point at the indices involved):
        //
        //           remove         add
        //             V             V
        //         | 1 |---|    2    |    3    |
        // src = [ a b * * c d e f g i j k l m ]
        // dst = [ a b c d e f g + i j k l m ]
        //         | 1 |    2    |-|    3    |
        val srcIndexAfterRemove = removeIndex + 2
        require(srcIndexAfterRemove <= addIndex)

        // removing two, adding one
        val dst = kotlin.arrayOfNulls<Any>(src.size - 1)

        arraycopy(src, 0, dst, 0, removeIndex)

        arraycopy(src, srcIndexAfterRemove, dst, removeIndex, (addIndex - srcIndexAfterRemove))
        dst[addIndex - 2] = insertion
        arraycopy(src, addIndex, dst, (addIndex - 1), (src.size - addIndex))

        return dst
    }

    fun arrayCopyAndRemovePairAndElement(src: Array<Any?>, pairIndex: Int, laterIndex: Int): Array<Any?> {
        // The copy is made in three chunks (the pipes point at the indices involved):
        //
        //            pair        element
        //             V             V
        //         | 1 |---|    2    |-|   3   |
        // src = [ a b * * c d e f g * h i j k ]
        // dst = [ a b c d e f g h i j k ]
        //         | 1 |    2    |   3   |
        val srcIndexAfterPair = pairIndex + 2
        require(srcIndexAfterPair <= laterIndex)

        val dst = kotlin.arrayOfNulls<Any>(src.size - 3)

        arraycopy(src, 0, dst, 0, pairIndex)

        arraycopy(src, srcIndexAfterPair, dst, pairIndex, (laterIndex - srcIndexAfterPair))
        arraycopy(src, laterIndex + 1, dst, laterIndex - 2, (src.size - laterIndex - 1))

        return dst
    }


    /**
     * A strangely specific array copy. Add the two elements at `addIndex`
     * and `addIndex+1`; and remove an element at `removeIndex`;
     * All indices are in terms of the *source* array.
     */
    fun arrayCopyAndInsertPairAndRemove(
        src: Array<Any?>,
        addIndex: Int,
        first: Any?,
        second: Any?,
        removeIndex: Int,
    ): Array<Any?> {
        // The copy is made in three chunks (the pipes point at the indices involved):
        //
        //            add     remove
        //             V         V
        //         | 1 |    2    |-|   3   |
        // src = [ a b e f g h i J k l m n ]
        // dst = [ a b C D e f g h i k l m n ]
        //         | 1 |---|    2    |   3   |
        require(addIndex <= removeIndex)

        // adding two, removing one
        val dst = kotlin.arrayOfNulls<Any>(src.size + 1)

        arraycopy(src, 0, dst, 0, addIndex)
        dst[addIndex] = first
        dst[addIndex + 1] = second

        arraycopy(src, addIndex, dst, (addIndex + 2), (removeIndex - addIndex))

        val srcIndexAfterRemoval = removeIndex + 1
        arraycopy(src, srcIndexAfterRemoval, dst, (removeIndex + 2), (src.size - srcIndexAfterRemoval))

        return dst
    }


    fun <K, V> arrayCopyAndInsertPairAndElement(
        src: Array<Any?>,
        pairIndex: Int,
        first: Any?,
        second: Any?,
        elementIndex: Int,
        element: Any?,
    ): Array<Any?> {
        // The copy is made in three chunks (the pipes point at the indices involved):
        //
        //            pair     element
        //             V         V
        //         | 1 |    2    |   3   |
        // src = [ a b e f g h i k l m n ]
        // dst = [ a b + + e f g h i + k l m n ]
        //         | 1 |---|    2    |-|   3   |
        require(pairIndex <= elementIndex)

        val dst = kotlin.arrayOfNulls<Any>(src.size + 3)

        arraycopy(src, 0, dst, 0, pairIndex)
        dst[pairIndex] = first
        dst[pairIndex + 1] = second

        arraycopy(src, pairIndex, dst, (pairIndex + 2), (elementIndex - pairIndex))
        dst[elementIndex + 2] = element
        arraycopy(src, elementIndex, dst, (elementIndex + 3), (src.size - elementIndex))

        return dst
    }

    fun copyToLength(src: Array<Any?>, newLength: Int): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(newLength)
        arraycopy(src, 0, dst, 0, min(src.size, newLength))
        return dst
    }

    fun <T> swapOut(array: Array<Any?>, index: Int, value: T?): T? {
        val old = array[index] as T?
        array[index] = value
        return old
    }

    fun appendRanges(
        src1: Array<Any?>, start1: Int, len1: Int,
        src2: Array<Any?>, start2: Int, len2: Int,
    ): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(len1 + len2)
        arraycopy(src1, start1, dst, 0, len1)
        arraycopy(src2, start2, dst, len1, len2)

        return dst
    }

    fun <K> appendRanges(
        src1: Array<Any?>, start1: Int, len1: Int,
        src2: Array<Any?>, start2: Int, len2: Int,
        src3: Array<Any?>, start3: Int, len3: Int,
    ): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(len1 + len2 + len3)
        arraycopy(src1, start1, dst, 0, len1)
        arraycopy(src2, start2, dst, len1, len2)
        arraycopy(src3, start3, dst, (len1 + len2), len3)

        return dst
    }

    fun appendRanges(
        src1: Array<Any?>, start1: Int, len1: Int,
        src2: Array<Any?>, start2: Int, len2: Int,
        src3: Array<Any?>, start3: Int, len3: Int,
        src4: Array<Any?>, start4: Int, len4: Int,
        src5: Array<Any?>, start5: Int, len5: Int,
    ): Array<Any?> {
        val dst = kotlin.arrayOfNulls<Any>(len1 + len2 + len3 + len4 + len5)

        var start = 0
        arraycopy(src1, start1, dst, start, len1)
        arraycopy(src2, start2, dst, (len1.let { start += it; start }), len2)
        arraycopy(src3, start3, dst, (len2.let { start += it; start }), len3)
        arraycopy(src4, start4, dst, (len3.let { start += it; start }), len4)
        arraycopy(src5, start5, dst, (start + len4), len5)

        return dst
    }

    fun copyAndMoveToBack(
        array: Array<Any?>, indexOld: Int, indexNew: Int,
        elementNew: Any?,
    ): Array<Any?> {
        require(indexOld <= indexNew)
        if (indexNew == indexOld) {
            return copyAndSet(array, indexNew, elementNew)
        } else {
            val arrayNew = kotlin.arrayOfNulls<Any>(array.size)
            arraycopy(array, 0, arrayNew, 0, indexOld)
            arraycopy(array, indexOld + 1, arrayNew, indexOld, indexNew - indexOld)
            arrayNew[indexNew] = elementNew
            arraycopy(array, indexNew + 1, arrayNew, indexNew + 1, array.size - indexNew - 1)
            return arrayNew
        }
    }

    fun copyAndMoveToFront(
        array: Array<Any?>, indexOld: Int, indexNew: Int,
        elementNew: Any?,
    ): Array<Any?> {
        require(indexOld >= indexNew)
        if (indexNew == indexOld) {
            return copyAndSet(array, indexOld, elementNew)
        } else {
            val arrayNew = kotlin.arrayOfNulls<Any>(array.size)
            arraycopy(array, 0, arrayNew, 0, indexNew)
            arrayNew[indexNew] = elementNew
            arraycopy(array, indexNew, arrayNew, indexNew + 1, indexOld - indexNew)
            arraycopy(array, indexOld + 1, arrayNew, indexOld + 1, array.size - indexOld - 1)
            return arrayNew
        }
    }

    fun copyAndSet(array: Array<Any?>, index: Int, elementNew: Any?): Array<Any?> {
        val arrayNew = arrayOfNulls<Any>(array.size)
        arraycopy(array, 0, arrayNew, 0, array.size)
        arrayNew[index] = elementNew
        return arrayNew
    }


    /**
     * A soft maximum array length imposed by array growth computations.
     * Some JVMs (such as HotSpot) have an implementation limit that will cause
     *
     * OutOfMemoryError("Requested array size exceeds VM limit")
     *
     * to be thrown if a request is made to allocate an array of some length near
     * Integer.MAX_VALUE, even if there is sufficient heap available. The actual
     * limit might depend on some JVM implementation-specific characteristics such
     * as the object header size. The soft maximum value is chosen conservatively so
     * as to be smaller than any implementation limit that is likely to be encountered.
     */
    val SOFT_MAX_ARRAY_LENGTH: Int = Int.Companion.MAX_VALUE - 8

    /**
     * Computes a new array length given an array's current length, a minimum growth
     * amount, and a preferred growth amount. The computation is done in an overflow-safe
     * fashion.
     *
     * This method is used by objects that contain an array that might need to be grown
     * in order to fulfill some immediate need (the minimum growth amount) but would also
     * like to request more space (the preferred growth amount) in order to accommodate
     * potential future needs. The returned length is usually clamped at the soft maximum
     * length in order to avoid hitting the JVM implementation limit. However, the soft
     * maximum will be exceeded if the minimum growth amount requires it.
     *
     * If the preferred growth amount is less than the minimum growth amount, the
     * minimum growth amount is used as the preferred growth amount.
     *
     * The preferred length is determined by adding the preferred growth amount to the
     * current length. If the preferred length does not exceed the soft maximum length
     * (SOFT_MAX_ARRAY_LENGTH) then the preferred length is returned.
     *
     * If the preferred length exceeds the soft maximum, we use the minimum growth
     * amount. The minimum required length is determined by adding the minimum growth
     * amount to the current length. If the minimum required length exceeds Integer.MAX_VALUE,
     * then this method throws OutOfMemoryError. Otherwise, this method returns the greater of
     * the soft maximum or the minimum required length.
     *
     * Note that this method does not do any array allocation itself; it only does array
     * length growth computations. However, it will throw OutOfMemoryError as noted above.
     *
     * Note also that this method cannot detect the JVM's implementation limit, and it
     * may compute and return a length value up to and including Integer.MAX_VALUE that
     * might exceed the JVM's implementation limit. In that case, the caller will likely
     * attempt an array allocation with that length and encounter an OutOfMemoryError.
     * Of course, regardless of the length value returned from this method, the caller
     * may encounter OutOfMemoryError if there is insufficient heap to fulfill the request.
     *
     * @param oldLength   current length of the array (must be nonnegative)
     * @param minGrowth   minimum required growth amount (must be positive)
     * @param prefGrowth  preferred growth amount
     * @return the new array length
     * @throws OutOfMemoryError if the new length would exceed Integer.MAX_VALUE
     */
    fun newLength(oldLength: Int, minGrowth: Int, prefGrowth: Int): Int {
        // preconditions not checked because of inlining
        // assert oldLength >= 0
        // assert minGrowth > 0

        val prefLength = oldLength + max(minGrowth, prefGrowth) // might overflow
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            return prefLength
        } else {
            // put code cold in a separate method
            return hugeLength(oldLength, minGrowth)
        }
    }

    private fun hugeLength(oldLength: Int, minGrowth: Int): Int {
        val minLength = oldLength + minGrowth
        if (minLength < 0) { // overflow
            throw OutOfMemoryError("Required array length $oldLength + $minGrowth is too large")
        } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
            return SOFT_MAX_ARRAY_LENGTH
        } else {
            return minLength
        }
    }
}

inline fun <reified T> v(vararg values:T) = values

object V{
    operator fun get(vararg values:Int) = intArrayOf(*values)

    operator fun get(vararg values:Long) = longArrayOf(*values)

    operator fun get(vararg values:Float) = floatArrayOf(*values)

    operator fun get(vararg values:Double) = doubleArrayOf(*values)

    operator fun get(vararg values:Boolean) = booleanArrayOf(*values)

    operator fun get(vararg values:Byte) = byteArrayOf(*values)

    inline operator fun <reified T> get(vararg values:T) = values
}

/**
 * Builds a new read-only List by populating a MutableList using the given builderAction and returning a read-only list with the same elements.
 * The list passed as a receiver to the builderAction is valid only inside that function. Using it outside of the function produces an unspecified behavior.
 * The returned list is serializable (JVM).
 *
 * The list is then converted into an array.
 */
inline fun <reified T> buildArray(builderAction: MutableList<T>.() -> Unit): Array<T> = buildList(builderAction).toTypedArray()


fun BooleanArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
      println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun ByteArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun CharArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun ShortArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun IntArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun LongArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun FloatArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}
fun DoubleArray.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}

fun <T> Array<T>.println(isDebug:Boolean=true,prefix:CharSequence="[",separator:CharSequence=",",postfix: CharSequence="]"){
    if(isDebug)
        println(joinToString(separator = separator, prefix = prefix, postfix = postfix))
}

